require 'fileutils'
require 'json'

RAKE_DBG = true
COMPILE_DBG = true

DEVICE_PORT  = '/dev/tty.usbserial-1110'
PROJECT_NAME = 'firmware'
MCU_IRAM = 256
MCU_XRAM = 1024
MCU_CODE_SIZE = 8192

USER_CSOURCES = 'src'
USER_INCLUDES = 'include'
USER_INCFLAGS = ["-I#{USER_INCLUDES}", "-I#{USER_CSOURCES}"]

LIB_DIR  = 'lib'
LIB_LIST = Dir.children(LIB_DIR).select { |dir| File.directory? "#{LIB_DIR}/#{dir}" }
LIB_INCFLAGS = LIB_LIST.map { |lib| "-I#{LIB_DIR}/#{lib}/include" }

LIB_FLAGS = ["__CONF_FOSC=11059200UL",
             "__CONF_MCU_MODEL=MCU_MODEL_STC8G1K08",
             "__CONF_CLKDIV=0x00",
             "__CONF_IRCBAND=0x01",
             "__CONF_VRTRIM=0x1F",
             "__CONF_IRTRIM=0xB5",
             "__CONF_LIRTRIM=0x00"]

BUILD_PATH = 'build'

CC = `which sdcc`.strip
AR = `which sdar`.strip
SDRANLIB   = `which sdranlib`.strip
PACKIHX    = `which packihx`.strip
FLASH      = `which stcgal`.strip
ARCH_FLAGS = "-mmcs51"
OPT        = "--opt-code-size"
CSTD       = "--std-sdcc99"
CC_CFLAGS  = [ARCH_FLAGS, OPT, CSTD] + LIB_FLAGS.map { |flag| "-D#{flag}" }
CC_CFLAGS  = CC_CFLAGS + ["--debug"] if COMPILE_DBG
LD_CFLAGS  = [ARCH_FLAGS, OPT,
              "--iram-size #{MCU_IRAM}",
              "--xram-size #{MCU_XRAM}",
              "--code-size #{MCU_CODE_SIZE}",
              "--out-fmt-ihx"]
TGT_FLAGS = ["-rcs"]

def shell(*cmd)
  print cmd.join(" "), "\n" if RAKE_DBG
  system *cmd
end

def dir?(path, &block)
  yield path if File.directory? path
end

def file?(dir, ext, &block)
  files = []
  Dir.each_child(dir) { |f| (block ? yield(f) : files << "#{dir}/#{f}") if File.extname(f) == ext }
  return files
end

def extr(f, ext)
  f.gsub(File.extname(f), ext)
end

def build_file(source, target, custom=[])
  puts "Compile #{source}"
  shell CC,
        "-c", source,
        *CC_CFLAGS,
        *custom,
        *LIB_INCFLAGS,
        "-o", target
end

def build_lib(name, path)
  lib_file = []
  lib_path = "#{BUILD_PATH}/#{name}"
  
  FileUtils.mkdir_p(lib_path)
  
  file?("#{path}/src", ".c") { |f|
    build_file("#{path}/src/#{f}", "#{lib_path}/#{extr f, '.rel'}")
    lib_file << "#{lib_path}/#{extr f, '.rel'}"
  }

  lib_file_path = "#{BUILD_PATH}/lib/#{name}.lib"
  FileUtils.mkdir_p("#{BUILD_PATH}/lib")
  
  puts "Creating static lib #{name}"
  shell AR, *TGT_FLAGS, lib_file_path, *lib_file
  shell SDRANLIB, lib_file_path
end

desc "Libraries should be stored at lib dir."
task :build_lib do
  Dir.each_child(LIB_DIR) do |dir|
    dir?("#{LIB_DIR}/#{dir}") { |path| build_lib(dir, path) }
  end
end

task :build_user do
  puts "Build User Files:"
  file?('.', '.c') { |f| build_file(f, "#{BUILD_PATH}/#{extr f, '.rel'}", USER_INCFLAGS) }
  file?('src', '.c') { |f| build_file("src/#{f}", "#{BUILD_PATH}/#{extr f, '.rel'}", USER_INCFLAGS) }
end

task :build_project => [:build_lib, :build_user] do
  lib_files = []; file?("#{BUILD_PATH}/lib", '.lib') { |f| lib_files << "#{BUILD_PATH}/lib/#{f}" }
  usr_files = []; file?("#{BUILD_PATH}", '.rel') { |f| usr_files << "#{BUILD_PATH}/#{f}" }
  target    = "#{BUILD_PATH}/#{PROJECT_NAME}.ihx"
  shell CC, *LD_CFLAGS, *usr_files, *lib_files, "-o", target
end

task :build_main do
  f = "main.c"
  build_file("src/#{f}", "#{BUILD_PATH}/#{extr f, '.rel'}", USER_INCFLAGS)
end

task :main => [:build_lib, :build_main] do
  lib_files = []; file?("#{BUILD_PATH}/lib", '.lib') { |f| lib_files << "#{BUILD_PATH}/lib/#{f}" }
  usr_files = ["#{BUILD_PATH}/main.rel"]
  target    = "#{BUILD_PATH}/main.ihx"
  shell CC, *LD_CFLAGS, *usr_files, *lib_files, "-o", target  
end

task :build_i2c do
  f = "main-i2c.c"
  build_file("src/#{f}", "#{BUILD_PATH}/#{extr f, '.rel'}", USER_INCFLAGS)
end

task :i2c => [:build_lib, :build_i2c] do
  lib_files = []; file?("#{BUILD_PATH}/lib", '.lib') { |f| lib_files << "#{BUILD_PATH}/lib/#{f}" }
  usr_files = ["#{BUILD_PATH}/main-i2c.rel"]
  target    = "#{BUILD_PATH}/main-i2c.ihx"
  shell CC, *LD_CFLAGS, *usr_files, *lib_files, "-o", target  
end

task :flash_project => [:build_project] do
  shell FLASH, "-P", "stc8g", "-p", DEVICE_PORT, "#{BUILD_PATH}/#{PROJECT_NAME}.hex", "-t", "11059"
end

task :clean do
  shell "rm", "-rf", "#{BUILD_PATH}/*"
end

task :generate_compile_commands do
  compile_commands = []
  directory = File.absolute_path('.')

  # Lib
  Dir.each_child(LIB_DIR) do |dir|
    dir?("#{LIB_DIR}/#{dir}") do |path|
      lib_file = []
      lib_path = "#{BUILD_PATH}/#{dir}"
      file?("#{path}/src", ".c") do |lib_f|
        file = File.absolute_path("#{path}/src/#{lib_f}")
        output = File.absolute_path("#{lib_path}/#{extr lib_f, '.rel'}")
        compile_commands << {
          directory: directory,
          command: [CC, "-c", file, *CC_CFLAGS, *LIB_INCFLAGS,
                    "-o", output].join(" "),
          file: file,
          output: output
        }
        lib_file << lib_f
      end
    end
  end

  # User
    file?('src', '.c') do |f|
    file   = File.absolute_path(f)
    output = File.absolute_path("#{BUILD_PATH}/#{extr f, '.rel'}")
    compile_commands << {
      directory: directory,
      command: [CC, '-c', file, *CC_CFLAGS, *USER_INCFLAGS, *LIB_INCFLAGS, '-o', output].join(" "),
      file: file,
      output: output
    }
  end
  
  file?('src', '.c') do |f|
    file   = File.absolute_path(f)
    output = File.absolute_path("#{BUILD_PATH}/#{extr f, '.rel'}")
    compile_commands << {
      directory: directory,
      command: [CC, '-c', file, *CC_CFLAGS, *USER_INCFLAGS, *LIB_INCFLAGS, '-o', output].join(" "),
      file: file,
      output: output
    }
  end

  puts compile_commands.to_json
end
